<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount } from 'vue';
import BpmnModeler from 'bpmn-js/lib/Modeler';
import propertiesPanelModule from 'bpmn-js-properties-panel';
import propertiesProviderModule from 'bpmn-js-properties-panel/lib/provider/camunda';
import camundaModdleDescriptor from 'camunda-bpmn-moddle/resources/camunda.json';
import { Icon, Button as TButton, Space as TSpace, MessagePlugin } from 'tdesign-vue-next';

// 导入 BPMN 相关样式
import 'bpmn-js/dist/assets/diagram-js.css';
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css';
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css';
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css';
import 'bpmn-js-properties-panel/dist/assets/bpmn-js-properties-panel.css';

const props = defineProps<{
  modelId?: string;
  xml?: string;
}>();

const emit = defineEmits<{
  (e: 'save', xml: string): void;
  (e: 'deploy'): void;
}>();

// refs
const canvasRef = ref<HTMLDivElement | null>(null);
const panelRef = ref<HTMLDivElement | null>(null);
const bpmnModeler = ref<any>(null);

// 是否可以撤销/重做
const canUndo = ref(false);
const canRedo = ref(false);

// 初始化 BPMN 建模器
const initBpmnModeler = () => {
  if (!canvasRef.value || !panelRef.value) return;

  bpmnModeler.value = new BpmnModeler({
    container: canvasRef.value,
    propertiesPanel: {
      parent: panelRef.value
    },
    additionalModules: [
      propertiesPanelModule,
      propertiesProviderModule
    ],
    moddleExtensions: {
      camunda: camundaModdleDescriptor
    }
  });

  const eventBus = bpmnModeler.value.get('eventBus');
  eventBus.on('element.click', (event: { element: any }) => {
    const { element } = event;
    console.log('点击元素:', element);
  });

  // 监听命令栈变化
  const commandStack = bpmnModeler.value.get('commandStack');
  eventBus.on('commandStack.changed', () => {
    canUndo.value = commandStack.canUndo();
    canRedo.value = commandStack.canRedo();
  });
};

// 加载流程定义
const loadProcessDefinition = async () => {
  if (!bpmnModeler.value) return;

  try {
    if (props.xml) {
      await bpmnModeler.value.importXML(props.xml);
    } else {
      // 创建空白流程
      const emptyXml = `<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn">
  <bpmn:process id="Process_1" name="新建流程" isExecutable="true">
    <bpmn:startEvent id="StartEvent_1" name="开始" />
  </bpmn:process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
      <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
        <dc:Bounds x="179" y="159" width="36" height="36" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="186" y="202" width="22" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn:definitions>`;
      await bpmnModeler.value.importXML(emptyXml);
    }
    const canvas = bpmnModeler.value.get('canvas');
    canvas.zoom('fit-viewport');
  } catch (error) {
    console.error('加载流程定义失败:', error);
    MessagePlugin.error('加载流程定义失败');
  }
};

// 保存流程
const save = async () => {
  try {
    const { xml } = await bpmnModeler.value.saveXML({ format: true });
    emit('save', xml);
    MessagePlugin.success('保存成功');
  } catch (error) {
    console.error('保存失败:', error);
    MessagePlugin.error('保存失败');
  }
};

// 部署流程
const deploy = async () => {
  try {
    await save();
    emit('deploy');
    MessagePlugin.success('部署成功');
  } catch (error) {
    console.error('部署失败:', error);
    MessagePlugin.error('部署失败');
  }
};

// 放大
const zoomIn = () => {
  const canvas = bpmnModeler.value.get('canvas');
  canvas.zoom(canvas.zoom() + 0.1);
};

// 缩小
const zoomOut = () => {
  const canvas = bpmnModeler.value.get('canvas');
  canvas.zoom(canvas.zoom() - 0.1);
};

// 重置缩放
const resetZoom = () => {
  const canvas = bpmnModeler.value.get('canvas');
  canvas.zoom('fit-viewport');
};

// 撤销
const undo = () => {
  if (!bpmnModeler.value) return;
  const commandStack = bpmnModeler.value.get('commandStack');
  commandStack.undo();
};

// 重做
const redo = () => {
  if (!bpmnModeler.value) return;
  const commandStack = bpmnModeler.value.get('commandStack');
  commandStack.redo();
};

// 下载 XML
const downloadXML = async () => {
  try {
    const { xml } = await bpmnModeler.value.saveXML({ format: true });
    const blob = new Blob([xml], { type: 'application/xml' });
    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = url;
    link.download = 'process.bpmn';
    link.click();
    URL.revokeObjectURL(url);
  } catch (error) {
    console.error('下载 XML 失败:', error);
    MessagePlugin.error('下载 XML 失败');
  }
};

// 下载 SVG
const downloadSVG = async () => {
  try {
    const { svg } = await bpmnModeler.value.saveSVG();
    const blob = new Blob([svg], { type: 'image/svg+xml' });
    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = url;
    link.download = 'process.svg';
    link.click();
    URL.revokeObjectURL(url);
  } catch (error) {
    console.error('下载 SVG 失败:', error);
    MessagePlugin.error('下载 SVG 失败');
  }
};

onMounted(() => {
  initBpmnModeler();
  loadProcessDefinition();
});

onBeforeUnmount(() => {
  if (bpmnModeler.value) {
    bpmnModeler.value.destroy();
  }
});
</script>

<template>
  <div class="bpmn-designer">
    <div class="toolbar">
      <t-space>
        <t-button @click="save">
          <template #icon><icon name="save" /></template>
          保存
        </t-button>
        <t-button @click="deploy">
          <template #icon><icon name="play" /></template>
          部署
        </t-button>
        <t-button @click="zoomIn">
          <template #icon><icon name="zoom-in" /></template>
          放大
        </t-button>
        <t-button @click="zoomOut">
          <template #icon><icon name="zoom-out" /></template>
          缩小
        </t-button>
        <t-button @click="resetZoom">
          <template #icon><icon name="refresh" /></template>
          重置
        </t-button>
        <t-button :disabled="!canUndo" @click="undo">
          <template #icon><icon name="undo" /></template>
          撤销
        </t-button>
        <t-button :disabled="!canRedo" @click="redo">
          <template #icon><icon name="redo" /></template>
          重做
        </t-button>
        <t-button @click="downloadXML">
          <template #icon><icon name="download" /></template>
          下载XML
        </t-button>
        <t-button @click="downloadSVG">
          <template #icon><icon name="image" /></template>
          下载SVG
        </t-button>
      </t-space>
    </div>
    <div class="content">
      <div ref="canvasRef" class="canvas"></div>
      <div ref="panelRef" class="panel"></div>
    </div>
  </div>
</template>

<style lang="less" scoped>
.bpmn-designer {
  height: 100%;
  display: flex;
  flex-direction: column;

  .toolbar {
    padding: 8px;
    border-bottom: 1px solid var(--td-component-border);
    display: flex;
    gap: 8px;
  }

  .content {
    flex: 1;
    display: flex;
    overflow: hidden;

    .canvas {
      flex: 1;
      height: 100%;
      position: relative;
      background: url('')
        repeat !important;
    }

    .panel {
      width: 300px;
      height: 100%;
      border-left: 1px solid var(--td-component-border);
      overflow-y: auto;
    }
  }
}

:deep(.bpp-properties-panel) {
  --color-grey-225-10-15: var(--td-bg-color-container);
  --color-grey-225-10-35: var(--td-component-border);
  --color-grey-225-10-55: var(--td-text-color-secondary);
  --color-grey-225-10-75: var(--td-text-color-primary);
  --color-grey-225-10-80: var(--td-brand-color);
  --color-grey-225-10-85: var(--td-brand-color-hover);
  --color-grey-225-10-90: var(--td-brand-color-active);
  --color-grey-225-10-95-7: var(--td-brand-color-light);
  --color-grey-225-10-97: var(--td-brand-color-focus);

  background-color: var(--td-bg-color-container);
  border-left: 1px solid var(--td-component-border);
}

:deep(.djs-palette) {
  background: var(--td-bg-color-container);
  border: 1px solid var(--td-component-border);
}

:deep(.djs-popup) {
  background: var(--td-bg-color-container);
  border: 1px solid var(--td-component-border);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}

:deep(.djs-context-pad) {
  background: var(--td-bg-color-container);
  border: 1px solid var(--td-component-border);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}

:deep(.djs-overlay) {
  background: var(--td-bg-color-container);
  border: 1px solid var(--td-component-border);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}

:deep(.djs-minimap) {
  background: var(--td-bg-color-container);
  border: 1px solid var(--td-component-border);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}

:deep(.djs-search) {
  background: var(--td-bg-color-container);
  border: 1px solid var(--td-component-border);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}

:deep(.djs-label) {
  font-family: var(--td-font-family);
  font-size: var(--td-font-size-base);
  color: var(--td-text-color-primary);
}

:deep(.djs-tooltip) {
  font-family: var(--td-font-family);
  font-size: var(--td-font-size-base);
  color: var(--td-text-color-primary);
  background: var(--td-bg-color-container);
  border: 1px solid var(--td-component-border);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}
</style> 